#ifndef __PX_PAD_TYPE_CONV_H__
#define __PX_PAD_TYPE_CONV_H__

#ifndef _WIN32
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <utility>
#include <string.h>
#include <string>
#include <algorithm>

namespace PadLog
{
    constexpr int32_t DefBuffSize = 2 * 1024 * 1024;          //2^n
    constexpr int32_t ModNum = DefBuffSize - 1;

    const char* const digits = "9876543210123456789";
    const char* const zero = digits + 9;

    enum DataType
    {
        TypeBool = 0,
        TypeInt8 = 1,
        TypeUint8 = 2,
        TypeInt16 = 3,
        TypeUint16 = 4,
        TypeInt32 = 5,
        TypeUint32 = 6,
        TypeInt64 = 7,
        TypeUint64 = 8,
        TypeLLong = 9,
        TypeULLong = 10,
        TypeFloat = 11,
        TypeDouble = 12,
        TypePoint = 13,
        TypeChars = 14,
        TypeConstChars = 15,
        TypeString = 16,
        TypeArrChars = 17,
    };

    struct Param_Type
    {
        int32_t arg_type = 0;
        int32_t arg_size = 0;
    };

    class PadConv
    {
    public:
        template <int Num>
        static inline DataType _ArgSize(const char(&str)[Num], int32_t& arg_len)
        {
            arg_len = Num;
            return TypeArrChars;
        }

        template <int Num>
        static inline DataType _ArgSize(char(&str)[Num], int32_t& arg_len)
        {
            arg_len = Num;
            return TypeArrChars;
        }

#if __GNUC__ >= 9
        static inline DataType _ArgSize(char(&str)[], int32_t& arg_len)
        {
            arg_len = 0;
            while (str[arg_len] != '\0' || arg_len <= 256)
                ++arg_len;
            return TypeChars;
        }

        static inline DataType _ArgSize(const char(&str)[], int32_t& arg_len)
        {
            arg_len = 0;
            while (str[arg_len] != '\0' || arg_len <= 256)
                ++arg_len;
            return TypeChars;
        }
#endif

        template <typename T>
        static inline typename std::enable_if<std::is_same<T, const char*>::value
            || std::is_same<T, char*>::value || std::is_same<T, const char* const>::value, DataType>::type _ArgSize(T str, int32_t& arg_len)
        {
            if (str)
            {
                arg_len = strlen(str);
            }
            else
            {
                arg_len = 0;
            }
            return TypeChars;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, std::string>::value || std::is_same<typename std::remove_reference<T>::type, const std::string>::value
            , DataType>::type _ArgSize(T str, int32_t& arg_len)
        {
            arg_len = str.size();
            return TypeString;
        }

        template <typename T>
        static inline typename std::enable_if<std::is_pointer<T>::value && !std::is_same<T, char*>::value
            && !std::is_same<T, const char*>::value && !std::is_same<T, const char* const>::value, DataType>::type _ArgSize(T ptr, int32_t& arg_len)
        {
            (void)ptr;
            arg_len = sizeof(T);
            return TypePoint;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, double>::value || std::is_same<typename std::remove_reference<T>::type, const double>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeDouble;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, float>::value || std::is_same<typename std::remove_reference<T>::type, const float>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeFloat;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, bool>::value || std::is_same<typename std::remove_reference<T>::type, const bool>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeBool;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, int8_t>::value || std::is_same<typename std::remove_reference<T>::type, const int8_t>::value
            || std::is_same<typename std::remove_reference<T>::type, char>::value || std::is_same<typename std::remove_reference<T>::type, const char>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeInt8;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, uint8_t>::value || std::is_same<typename std::remove_reference<T>::type, unsigned char>::value
            || std::is_same<typename std::remove_reference<T>::type, const uint8_t>::value || std::is_same<typename std::remove_reference<T>::type, const unsigned char>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeUint8;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, int16_t>::value || std::is_same<typename std::remove_reference<T>::type, const int16_t>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeInt16;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, uint16_t>::value || std::is_same<typename std::remove_reference<T>::type, const uint16_t>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeUint16;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, int32_t>::value || std::is_same<typename std::remove_reference<T>::type, const int32_t>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeInt32;
        }

        //枚举等效为int
        template <typename T>
        static typename std::enable_if<std::is_enum<typename std::remove_reference<T>::type>::value, DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(int);
            return TypeInt32;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, uint32_t>::value || std::is_same<typename std::remove_reference<T>::type, const uint32_t>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeUint32;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, int64_t>::value || std::is_same<typename std::remove_reference<T>::type, const int64_t>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeInt64;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, uint64_t>::value || std::is_same<typename std::remove_reference<T>::type, const uint64_t>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeUint64;
        }

#ifdef _WIN32
        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, long>::value || std::is_same<typename std::remove_reference<T>::type, const long>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeInt32;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, unsigned long>::value || std::is_same<typename std::remove_reference<T>::type, const unsigned long>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeUint32;
        }
#endif

#ifndef _WIN32
        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, long long>::value || std::is_same<typename std::remove_reference<T>::type, const long long>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeLLong;
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, unsigned long long>::value || std::is_same<typename std::remove_reference<T>::type, const unsigned long long>::value
            , DataType>::type _ArgSize(T&& data, int32_t& arg_len)
        {
            (void)data;
            arg_len = sizeof(T);
            return TypeULLong;
        }
#endif

        template<int index, int PNum>
        static inline int32_t ArgSize(Param_Type(&PArr)[PNum])
        {
            (void)PArr;
            return 0;
        }

        template <int index, int PNum, typename T>
        static inline int32_t ArgSize(Param_Type(&PArr)[PNum], T&& param)
        {
            int32_t arg_len = 0;
            int _type = _ArgSize(std::forward<T>(param), arg_len);
            PArr[index].arg_type = _type;
            PArr[index].arg_size = arg_len;
            return arg_len + 8;     //4字节表示参数type占用大小   4字节表示arg总长庿 arg_len表示arg实际的空闿
        }

        template <int index, int PNum, int Num>
        static inline int32_t ArgSize(Param_Type(&PArr)[PNum], char(&arr)[Num])
        {
            (void)PArr;
            (void)arr;
            int32_t arg_len = Num;
            PArr[index].arg_type = TypeArrChars;
            PArr[index].arg_size = arg_len;
            return arg_len + 8;
        }

        template <int index, int PNum, int Num>
        static inline int32_t ArgSize(Param_Type(&PArr)[PNum], const char(&arr)[Num])
        {
            (void)PArr;
            (void)arr;
            int32_t arg_len = Num;
            PArr[index].arg_type = TypeArrChars;
            PArr[index].arg_size = arg_len;
            return arg_len + 8;
        }

        template <int index, int PNum, typename T, typename ...Ts>
        static int32_t ArgSize(Param_Type(&PArr)[PNum], T&& param, Ts&&... Params)
        {
            return ArgSize<index>(PArr, std::forward<T>(param)) + ArgSize<index + 1>(PArr, std::forward<Ts>(Params)...);
        }

        template <int index, int PNum, typename ...Ts>
        static int32_t _ArgSize_(Param_Type(&PArr)[PNum], Ts&&... Params)
        {
            return ArgSize<index>(PArr, std::forward<Ts>(Params)...);
        }

        template <typename T>
        static inline typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, const char* const>::value
            || std::is_same<typename std::remove_reference<T>::type, const char*>::value
            || std::is_same<typename std::remove_reference<T>::type, char*>::value
            , void>::type CopyStr(T&& str, char* buf, int mod = ModNum)
        {
            if (str)
            {
                for (int i = 0; *(str + i) != '\0'; ++i)
                {
                    *(buf + i) = *(str + i);
                }
            }
        }

        template <typename T>
        static inline typename std::enable_if
            <std::is_same<typename std::remove_reference<T>::type, std::string>::value
            || std::is_same<typename std::remove_reference<T>::type, const std::string>::value
            , void>::type CopyStr(T&& str, char* buf, int mod = ModNum)
        {
            memcpy(buf, str.c_str(), str.size());
        }

        template <int Num>
        static inline void CopyStr(const char(&arr)[Num], char* buf, int mod = ModNum)
        {
            memcpy(buf, arr, Num);
        }

#if __GNUC__ >= 9
        static inline void CopyStr(const char(&arr)[], char* buf, int mod = ModNum)
        {
            int i = 0;
            while (arr[i] != '\0' || i <= 256)
            {
                *(buf + i) = arr[i];
                i += 1;
            }
        }
#endif

        template <typename T>
        static inline typename std::enable_if<!std::is_same<typename std::remove_reference<T>::type, char*>::value
            && !std::is_same<typename std::remove_reference<T>::type, const char* const>::value
            && !std::is_same<typename std::remove_reference<T>::type, const char*>::value
            && !std::is_same<typename std::remove_reference<T>::type, std::string>::value
            && !std::is_same<typename std::remove_reference<T>::type, const std::string>::value
            && !std::is_enum<typename std::remove_reference<T>::type>::value
            , void>::type CopyStr(T val, char* buf, int mod = ModNum)
        {
            T* p = (T*)buf;
            *p = val;
        }

        template <typename T>
        static typename std::enable_if<std::is_enum<typename std::remove_reference<T>::type>::value, void>::type CopyStr(T val, char* buf, int mod = ModNum)
        {
            int* p = (int*)buf;
            *p = (int)val;
        }

        template <typename T>
        static typename std::enable_if<
            std::is_same<typename std::remove_reference<T>::type, const char* const>::value
            || std::is_same<typename std::remove_reference<T>::type, const char*>::value
            || std::is_same<typename std::remove_reference<T>::type, char*>::value
            || std::is_same<typename std::remove_reference<T>::type, std::string>::value
            || std::is_same<typename std::remove_reference<T>::type, const std::string>::value
            , void>::type CopyStr(T&& str, char* buf, int32_t len, uint64_t& pos, int mod = ModNum)
        {
            for (int i = 0; i < len; ++i)
            {
                buf[pos & mod] = str[i];
                pos += 1;
            }
        }

        template <int Num>
        static void CopyStr(const char(&arr)[Num], char* buf, int32_t len, uint64_t& pos, int mod = ModNum)
        {
            CopyStr((const char*)arr, buf, len, pos, mod);
        }

        template <typename T>
        static typename std::enable_if<!std::is_same<typename std::remove_reference<T>::type, char*>::value
            && !std::is_same<typename std::remove_reference<T>::type, const char* const>::value
            && !std::is_same<typename std::remove_reference<T>::type, const char*>::value
            && !std::is_same<typename std::remove_reference<T>::type, std::string>::value
            && !std::is_same<typename std::remove_reference<T>::type, const std::string>::value
            && !std::is_enum<typename std::remove_reference<T>::type>::value
            , void>::type CopyStr(T val, char* buf, int32_t len, uint64_t& pos, int mod = ModNum)
        {
            char* p = (char*)&val;
            for (int i = 0; i < len; ++i)
            {
                buf[pos & mod] = p[i];
                pos += 1;
            }
        }

        template <typename T>
        static typename std::enable_if<std::is_enum<typename std::remove_reference<T>::type>::value, void>::type
            CopyStr(T val, char* buf, int32_t len, uint64_t& pos, int mod = ModNum)
        {
            int _val = (int)val;
            char* p = (char*)&_val;
            for (int i = 0; i < len; ++i)
            {
                buf[pos & mod] = p[i];
                pos += 1;
            }
        }

        template<typename T, int Num>
        static int MyPrintf(T val, char(&arr)[Num])
        {
            int cnt = 0;
            T _val = val;
            char* p = arr;
            do
            {
                int lsd = _val % 10;
                _val /= 10;
                *p++ = zero[lsd];
                ++cnt;
            } while (_val != 0);

            if (val < 0)
            {
                *p++ = '-';
                ++cnt;
            }
            *p = '\0';
            std::reverse(arr, p);
            return cnt;
        }
    };
}
#endif